require 'rbconfig'
begin
  require 'rubygems'
  require 'rainbow'
rescue LoadError => e
  # pass
  class String
    def bright(*args)
      self
    end
  end
end

def say(*msg)
  puts(*msg) unless RakeFileUtils.verbose_flag == false
end
def say_bold(*msg)
  msg = msg.map(&:to_s).join "\n"
  msg = msg.bright if msg.respond_to?(:bright)
  puts(msg) unless RakeFileUtils.verbose_flag == false
end

def sh!(*args, &block)
  old_verbose = RakeFileUtils.verbose_flag
  begin
    RakeFileUtils.verbose_flag = nil if RakeFileUtils.verbose_flag == :default
    sh(*args, &block)
  ensure
    RakeFileUtils.verbose_flag = old_verbose
  end
end

def _(f, base = File.dirname(__FILE__))
  File.expand_path(f, base)
end


dl_ext    = RbConfig::CONFIG['DLEXT']
ext_dir   = _("lib/parser/ext")
parser_e  = _("fancy_parser.#{dl_ext}", ext_dir)
load_rb   = _("boot/load.rb")

namespace :parser do

  lexer_lex = _("lexer.lex", ext_dir)
  lexer_c   = _("lexer.c", ext_dir)
  parser_y  = _("parser.y", ext_dir)
  parser_c  = _("parser.c", ext_dir)
  extconf   = _("extconf.rb", ext_dir)
  makefile  = _("Makefile", ext_dir)

  file lexer_c => file(lexer_lex) do
    Dir.chdir(ext_dir) do
      say "Generating lexer source for Fancy"
      sh! 'flex', '--outfile', lexer_c, '--header-file=lexer.h', lexer_lex
    end
  end

  file parser_c => file(parser_y) do
    Dir.chdir(ext_dir) do
      say "Generating parser source for Fancy"
      sh! 'bison', '--output', parser_c, '-d', '-v', parser_y
    end
  end

  file makefile => file(extconf) do
    Dir.chdir(ext_dir) { sh! 'rbx', extconf }
  end

  desc "Generate parser source from flex/bison"
  task :generate => [parser_c, lexer_c, makefile]

  file parser_e => file(makefile) do
    say "Building parser bundle for Fancy"
    sh! 'make', '-C', ext_dir
  end

  desc "Compile the parser extension"
  task :compile => file(parser_e)

  desc "Removed generated parser sources"
  task :remove do
    say "Cleaning Fancy parser sources in #{ext_dir}"
    rm_f [_("parser.h", ext_dir), _("lexer.h", ext_dir)], :verbose => false
    rm_f [makefile, parser_c, lexer_c], :verbose => false
  end

  desc "Clean compiled files."
  task :clean do
    say "Cleaning Fancy parser in #{ext_dir}"
    rm_f Dir.glob(_("*.{o,so,rbc,log,output,bundle}", ext_dir)), :verbose => false
    rm_rf [_("conftest.dSYM", ext_dir)], :verbose => false
  end

end


namespace :compiler do

  boot_parser_e = _("boot/compiler/parser/ext/"+File.basename(parser_e))

  file boot_parser_e => file(parser_e) do
    mkdir_p File.dirname(boot_parser_e), :verbose => false
    cp parser_e, boot_parser_e, :verbose => false
  end

  desc "Clean Ruby-generated compiler"
  task :clean do
    say "Cleaning stable compiler in #{_("boot/compiler")}"
    rm_rf _("boot/compiler"), :verbose => false
  end
  desc "Clean parser"
  task :clean_parser => :clean do
    say "Cleaning Rubinius parser bundle and conftest"
    rm_f boot_parser_e, :verbose => false
    rm_rf _("boot/rbx-compiler/parser/conftest.dSYM"), :verbose => false
  end

  # STAGE 1: Ruby running the Ruby compiler.
  desc "Compile fancy using boot/rbx-compiler into boot/compiler"
  task :rootstrap => "compiler:rbx_parser:ext" do
    say_bold "Compiling Fancy into boot/compiler using Rubinius compiler from boot/rbx-compiler."

    output = _("boot/compiler")

    cmd = ['rbx -Xint']
    cmd << _("boot/rbx-compiler/compiler.rb")
    cmd << "--batch" if RakeFileUtils.verbose_flag == true
    cmd << "--output-path" << output

    src_path = ["--source-path", _("lib")]
    sources = Dir.glob(_("lib/**/*.fy"))
    system (cmd + src_path + sources).join(" ")

    src_path = ["--source-path", _("boot")]
    sources = Dir.glob(_("boot/*.fy"))
    system (cmd + src_path + sources).join(" ")

    # system "rbx", _("boot/rbx-compiler/compiler.rb"), _("boot/compile.fy")
    cp _("boot/compiler/compile.fyc"), _("boot/compile.fyc"), :verbose => false

  end

  # STAGE 2:
  # Ruby-generated bytecode running the Fancy compiler.
  # This makes sure that the Ruby compiler is sane.
  desc "Compile fancy using the stable compiler (within boot/compiler)"
  task :compile => file(boot_parser_e) do
    say_bold "Compiling Fancy using boot/compiler."

    cmd = ['rbx -Xint', load_rb]
    cmd << _("boot/compiler/boot.fyc")
    cmd << _("boot/compiler/compiler.fyc")
    cmd << _("boot/compiler/compiler/command.fyc")
    cmd << _("boot/compiler/compile.fyc")
    cmd << "--"
    cmd << "--batch" if RakeFileUtils.verbose_flag == true

    sources = Dir.glob("lib/**/*.fy")
    system (cmd + sources).join(" ")
  end

  # STAGE 3:
  # Fancy-generated bytecode running the Fancy compiler.
  # This makes sure that the Fancy compiler is sane.
  desc "Compile fancy using lib/ (in boot/compiler) compiler into boot/.wootstrap"
  task :wootstrap do
    say_bold "Compiling Fancy into boot/.wootstrap using boot/compiler."

    output = _("boot/.wootstrap")

    cmd = ['rbx -Xint', load_rb]
    cmd << _("lib/boot.fyc")
    cmd << _("lib/compiler.fyc")
    cmd << _("lib/compiler/command.fyc")
    cmd << _("boot/compiler/compile.fyc")
    cmd << "--"
    cmd << "--batch" if RakeFileUtils.verbose_flag == true
    cmd << "--output-path" << output

    sources = Dir.glob("lib/**/*.fy")
    system (cmd + sources).join(" ")

    mkdir_p _("parser/ext", output), :verbose => false
    cp parser_e, _("parser/ext", output), :verbose => false

    say_bold "Moving Fancy-compiled boot/.wootstrap into boot/compiler"

    rm_rf _("boot/compiler")
    mv _("boot/.wootstrap"), _("boot/compiler")
  end

  load("boot/rbx-compiler/parser/Rakefile")

  task :bootstrap => ["parser:generate", "rbx_parser:ext", file(boot_parser_e)] do
    ["compiler:rootstrap", "compiler:compile", "compiler:wootstrap"].each do |t|
      task(t).reenable
      task(t).execute
    end
  end

end

desc "Deletes all .rbc and .fyc files."
task :clean_compiled do
  compiled = Dir.glob(_ "**/*.{rbc,fyc}")
  rm_f compiled, :verbose => false
end

desc "Clean compiled files."
task :clean => [:"compiler:clean", :clean_compiled]

desc "Clean parser files"
task :clean_parser => ["parser:clean", "parser:remove", :"compiler:clean_parser"]

desc "Clean parser and compiled files"
task :clean_all => [:clean, :clean_parser]

def compile(source)
  cmd = ['rbx', _("boot/load.rb")]
  cmd << _("lib/boot.fyc")
  cmd << _("lib/compiler.fyc")
  cmd << _("lib/compiler/command.fyc")
  cmd << _("boot/compile.fyc")
  cmd << "--"
  cmd << "--batch" if RakeFileUtils.verbose_flag == true
  cmd << source.to_s
  sh! *cmd
end

sources = Dir.glob(_("{lib,boot}/**/*.fy")).map { |f| file f }
compiled = sources.map { |s| file((s.to_s+"c") => [s, file(parser_e)]) { compile s } }

task :bootstrap_if_needed do
  task(:bootstrap).invoke unless File.directory? _("boot/compiler")
end

task :compile => compiled

desc "Runs the test suite."
task :test do
  sh! _('bin/fspec')
end

task :tests do
  task(:test).invoke
end

task "tests/" do
  task(:test).invoke
end

task :bootstrap => ["compiler:bootstrap"]

desc "Bootstraps (if necessary) and compiles system"
task :default => [:bootstrap_if_needed, :compile]

desc "Runs all example files in examples/ dir"
task :examples do
  Dir.glob(_("examples/*.fy")).each do |f|
    puts "Running #{f}"
    puts
    sh! _('bin/fancy'), f
    puts
  end
end

task "examples/" do
  task(:examples).invoke
end
